﻿
#if NET5_0

using Dot5Demo.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;

namespace Dot5Demo.Converters
{
    /*
     * https://docs.microsoft.com/zh-cn/dotnet/standard/serialization/system-text-json-converters-how-to?pivots=dotnet-5-0
     如何遵循基本模式来创建转换器
        创建一个派生自 JsonConverter<T> 的类，其中 T 是要进行序列化和反序列化的类型。
        重写 Read 方法，以反序列化传入 JSON 并将其转换为类型 T。 使用传递给方法的 Utf8JsonReader 读取 JSON。
        重写 Write 方法以序列化 T 类型的传入对象。 使用传递给方法的 Utf8JsonWriter 写入 JSON。
        仅当需要时才重写 CanConvert 方法。 当要转换的类型属于类型 T 时，默认实现会返回 true。 
        因此，仅支持类型 T 的转换器不需要重写此方法。 
    如何遵循工厂模式来创建转换器
        创建一个从 JsonConverterFactory 派生的类。
        重写 CanConvert 方法，以在要转换的类型是转换器可处理的类型时返回 true。 例如，如果转换器适用于 List<T>，则它可能仅处理 List<int>、List<string> 和 List<DateTime>。
        重写 CreateConverter 方法，以返回将处理在运行时提供的要转换的类型的转换器类实例。
        创建 CreateConverter 方法实例化的转换器类。
            开放式泛型需要工厂模式，因为用于将对象与字符串相互转换的代码对于所有类型并不相同。 适用于开放式泛型类型（例如 List<T>）的转换器必须在幕后为封闭式泛型类型（例如 List<DateTime>）创建转换器。 
            必须编写代码来处理转换器可处理的每种封闭式泛型类型。
            Enum 类型类似于开放式泛型类型：适用于 Enum 的转换器必须在幕后为特定 Enum（例如WeekdaysEnum）创建转换器。
注册自定义转换器
    向 JsonSerializerOptions.Converters 集合添加转换器类的实例。
    将 [JsonConverter] 特性应用于需要自定义转换器的属性。
    将 [JsonConverter] 特性应用于表示自定义值类型的类或结构。
转换器注册优先级
    在序列化或反序列化过程中，按以下顺序（从最高优先级到最低优先级来列出）为每个 JSON 元素选择转换器：
    应用于属性的 [JsonConverter]。
    向 Converters 集合添加的转换器。
    应用于自定义值类型或 POCO 的 [JsonConverter]。
    如果在 Converters 集合中注册了适用于某种类型的多个自定义转换器，则使用第一个为 CanConvert 返回 true 的转换器。
    仅当未注册适用自定义转换器时，才会选择内置转换器。
     */

    /// <summary>
    /// 
    /// </summary>
    public class BasicPictureConverter : JsonConverter<Picture>
    {
        public override Picture Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            throw new NotImplementedException();
        }

        public override void Write(Utf8JsonWriter writer, Picture value, JsonSerializerOptions options)
        {
            throw new NotImplementedException();
        }
    }

    public class FactoryPictureConverter : JsonConverterFactory
    {
        public override bool CanConvert(Type typeToConvert)
        {
            throw new NotImplementedException();
        }

        public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
        {
            Type keyType = typeToConvert.GetGenericArguments()[0];
            Type valueType = typeToConvert.GetGenericArguments()[1];

            JsonConverter converter = (JsonConverter)Activator.CreateInstance(
                typeof(DictionaryEnumConverterInner<,>).MakeGenericType(
                    new Type[] { keyType, valueType }),
                BindingFlags.Instance | BindingFlags.Public,
                binder: null,
                args: new object[] { options },
                culture: null)!;

            return converter;
        }
    }

    public class DictionaryEnumConverterInner<TKey, TValue> :
            JsonConverter<Dictionary<TKey, TValue>> where TKey : struct, Enum
    {
        private readonly JsonConverter<TValue> _valueConverter;
        private readonly Type _keyType;
        private readonly Type _valueType;

        public DictionaryEnumConverterInner(JsonSerializerOptions options)
        {
            // For performance, use the existing converter if available.
            _valueConverter = (JsonConverter<TValue>)options
                .GetConverter(typeof(TValue));

            // Cache the key and value types.
            _keyType = typeof(TKey);
            _valueType = typeof(TValue);
        }

        public override Dictionary<TKey, TValue> Read(
            ref Utf8JsonReader reader,
            Type typeToConvert,
            JsonSerializerOptions options)
        {
            if (reader.TokenType != JsonTokenType.StartObject)
            {
                throw new JsonException();
            }

            var dictionary = new Dictionary<TKey, TValue>();

            while (reader.Read())
            {
                if (reader.TokenType == JsonTokenType.EndObject)
                {
                    return dictionary;
                }

                // Get the key.
                if (reader.TokenType != JsonTokenType.PropertyName)
                {
                    throw new JsonException();
                }

                string propertyName = reader.GetString();

                // For performance, parse with ignoreCase:false first.
                if (!Enum.TryParse(propertyName, ignoreCase: false, out TKey key) &&
                    !Enum.TryParse(propertyName, ignoreCase: true, out key))
                {
                    throw new JsonException(
                        $"Unable to convert \"{propertyName}\" to Enum \"{_keyType}\".");
                }

                // Get the value.
                TValue value;
                if (_valueConverter != null)
                {
                    reader.Read();
                    value = _valueConverter.Read(ref reader, _valueType, options)!;
                }
                else
                {
                    value = JsonSerializer.Deserialize<TValue>(ref reader, options)!;
                }

                // Add to dictionary.
                dictionary.Add(key, value);
            }

            throw new JsonException();
        }

        public override void Write(
            Utf8JsonWriter writer,
            Dictionary<TKey, TValue> dictionary,
            JsonSerializerOptions options)
        {
            writer.WriteStartObject();

            foreach ((TKey key, TValue value) in dictionary)
            {
                var propertyName = key.ToString();
                writer.WritePropertyName
                    (options.PropertyNamingPolicy?.ConvertName(propertyName) ?? propertyName);

                if (_valueConverter != null)
                {
                    _valueConverter.Write(writer, value, options);
                }
                else
                {
                    JsonSerializer.Serialize(writer, value, options);
                }
            }

            writer.WriteEndObject();
        }
    }
}

#endif